import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;

public class GeneralFunctions
{
	// Return histogram of grayscale image
    public static int[] imageHistogram(int[][] image)
    {
        int[] histogram = new int[256];
 
        for (int i = 0; i < image.length; ++i)
        {
                for (int j = 0; j < image[i].length; ++j)
                {
                histogram[image[i][j]]++;
            }
        }
 
        return histogram;
    }
    
	// Get binary treshold using Otsu's method
    private static int otsuTreshold(int[][] image)
    {
        int[] histogram = imageHistogram(image);
        int total = image.length * image[0].length;
 
        float sum = 0;
        
        for (int i = 0; i != 256; ++i) sum += i * histogram[i];
 
        float sumB = 0;
        int wB = 0;
        int wF = 0;
 
        float varMax = 0;
        int threshold = 0;
 
        for (int i = 0 ; i != 256 ; ++i)
        {
            wB += histogram[i];
            
            if (wB == 0) continue;
            
            wF = total - wB;
 
            if(wF == 0) break;
 
            sumB += (float)(i * histogram[i]);
            float mB = sumB / wB;
            float mF = (sum - sumB) / wF;
 
            float tempVar = (float) wB * (float) wF * (mB - mF) * (mB - mF);
 
            if (tempVar > varMax)
            {
                varMax = tempVar;
                threshold = i;
            }
        }
 
        return threshold;
    }
        
    // Converts a greyscale image to binary using Otsu's method
    public static int[][] binarize(int[][] array)
    {               
            int threshold = otsuTreshold(array);
            
            for (int i = 0; i != array.length; ++i)
            {
                    for (int j = 0; j != array[i].length; ++j)
                    {
                            if (array[i][j] >= threshold)
                            {
                                    array[i][j] = 1;
                            }
                            else
                            {
                                    array[i][j] = 0;
                            }
                    }
            }
            
            return array;
    }
	
	
	// Prints the image to a text file
	public static void printToFile(int[][] array, String fileName)
	{
		PrintWriter output = null;
		
		try
		{
			output = new PrintWriter(new FileOutputStream(fileName));
		}
		catch (FileNotFoundException e)
		{
			e.printStackTrace();
			System.exit(1);
		}
		
		for (int i = 0; i != array.length; ++i)
		{
			for (int j = 0; j != array[i].length; ++j)
			{
				output.print(array[i][j] + " ");
			}
			
			output.println();
		}
		
		output.close();
	}
	
	// Prints the image to a screen
	public static void printToScreen(int[][] array)
	{
		for (int i = 0; i != array.length; ++i)
		{
			for (int j = 0; j != array[i].length; ++j)
			{
				if (array[i][j] == 1)
				{
					System.out.print("@");
				}
				else
				{
					System.out.print(" ");
				}
			}
			
			System.out.println();
		}
	}
	
	// Prints multiple images of the same size to the screen
	public static void printToScreen(ArrayList<int[][]> array)
	{
		for (int i = 0; i != array.get(0).length; ++i)
		{
			for (int[][] anArray : array)
			{
				for (int j = 0; j != array.get(0)[i].length; ++j)
				{
					if (anArray[i][j] == 1)
					{
						System.out.print("@");
					}
					else
					{
						System.out.print(" ");
					}
				}
				
				System.out.print("\t");
				
			}
			
			System.out.println();
		}
	}
	
	// Find the geometric moment (for moment based methods)
	public static double findGeometricMoment(int[][] array, int p, int q)
	{
		double m_pq = 0;
		
		for (int i = 0; i != array.length; ++i)
		{
			for (int j = 0; j != array[0].length; ++j)
			{
				m_pq += Math.pow(j, p) * Math.pow(i, q) * array[i][j];
			}
		}
		
		return m_pq;
	}
	
	// Find the central moment (for moment based slant correction)
	public static double findCentralMoment(int[][] array, int p, int q)
	{
		double m_10 = findGeometricMoment(array, 1, 0);
		double m_01 = findGeometricMoment(array, 0, 1);
		double m_00 = findGeometricMoment(array, 0, 0);
		
		double x_c = m_10/m_00;
		double y_c = m_01/m_00;
		
		double u_pq = 0;
		
		for (int i = 0; i != array.length; ++i)
		{
			for (int j = 0; j != array[0].length; ++j)
			{
				u_pq += Math.pow((j - x_c), p) * Math.pow((i - y_c), q) * array[i][j];
			}
		}
		
		return u_pq;
	}
}